/**
 * Copyright: Copyright (C) Mike Reinhold 2010. All rights reserved.
 * Authors:   Mike Reinhold
 */
module tango.util.cipher.StreamCryptor;

import tango.util.cipher.Cipher;
import tango.util.cipher.Cryptor;

/** Cryptor implementation for wrapping Stream Ciphers */
class StreamCryptor: Cryptor {
	
	private {
		StreamCipher _cipher;
		SymmetricKey _key;
		IVParameters _iv;
	}
	
	/** Reset the cryptor and the underlying cipher */
	void reset() {
	}

	/** Returns: The name of the cryptor */
	string name() {
		if(_cipher is null) invalid("Cryptor: Not initialized");
		return _cipher.name;
	}
	
	/** Returns: If the Cryptor is in encrypt mode */
	bool encrypt() {
		return _cipher.encrypt;
	}

	/**
	 * Initialize the stream cryptor
	 * 
	 * Params:
	 *     cipher_ = The stream cipher to wrap in the cryptor
	 *     key_ = The symmetric key
	 *     iv_ = The initialization vector
	 */
	void init(StreamCipher cipher_, SymmetricKey key_, IVParameters iv_) {
		if(key_ is null) invalid("Cryptor: Key cannot be null");
		
		//resize the key material and initialization vector
		key_.setPreferredSize(cipher_);
		iv_.setIVSize(cipher_);
		_key = key_;
		_iv = iv_;
		
		//set the cipher
		_cipher = cipher_;
		
		//initialize the stream cipher
		_cipher.init(Cipher.ENCRYPT, _key);	
		_cipher.initIV(_iv);
	}

	/**
	 * Encrypt the input plaintext
	 * 
	 * Params:
	 *     input_ = The input plaintext to encrypt
	 *     resetPrior = If the Cryptor should reset before encrypting
	 *     
	 * Returns: The ciphertext output of the cryptor
	 */
	void[] encrypt(void[] input_, bool resetPrior = true) {
		void[] output = new void[input_.length];
		
		if(_cipher is null) invalid("Cryptor: Not initialized");
		
		//prepare
		if(resetPrior) {
			_cipher.reset();
			_cipher.init(Cipher.ENCRYPT, _key);
		}
		
		//encrypt
		output.length = input_.length;
		_cipher.update(input_, output);
		
		//return ciphertext
		return output;
	}

	/**
	 * Decrypt the input ciphertext
	 * 
	 * Params:
	 *     input_ = The input ciphertext to decrypt
	 *     resetPrior = If the Cryptor should reset before decrypting
	 *     
	 * Returns: The plaintext output of the cryptor
	 */
	void[] decrypt(void[] input_, bool resetPrior = true) {
		void[] output = new void[input_.length];
		
		if(_cipher is null) invalid("Cryptor: Not initialized");
		
		//prepare
		if(resetPrior) {
			_cipher.reset();	
			_cipher.init(Cipher.DECRYPT, _key);
		}
		
		//decrypt
		output.length = input_.length;
		_cipher.update(input_, output);
		
		//return plaintext
		return output;
	}
	
	StreamCryptor dup() {
		StreamCryptor b = new StreamCryptor();
		b._key = new SymmetricKey(this._key);
		b._iv = new IVParameters(this._iv);
		
		b._cipher= this._cipher.dup;
		
		return b;
	}
}